/** @file    BundleManager.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BundleManager.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * File containing methods for the wosh::BundleManager class.
 * The header for this class can be found in BundleManager.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/BundleManager.h>

 #include <core/ObjectAllocator.h>
 #include <core/BundleGeneric.h>
 #include <core/Settings.h>
 #include <core/WoshKernel.h>
 #include <core/Thread.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageFilter.h>
 #include <core/Fact.h>
 #include <core/SystemError.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/Functional.h>
 #include <core/ObjectFactory.h>
 #include <core/BundleRequirements.h>
 #include <core/PropertiesProvider.h>
 #include <core/InterfacesProvider.h>
 #include <core/MethodsProvider.h>
 #include <core/DataModels.h>
/*
 #include <core/MessageProcessingCommon.h>
 #include <core/FileSystem.h>

*/

using namespace std;
namespace wosh {

// Gamma singleton! static reference is allocated and maintained by WoshKernel
extern "C" {
	BundleManager* BundleManager::getInstance() { 
		if ( WoshKernel::getInstance() == NULL ) return NULL;
		return &WoshKernel::getInstance()->bundles();
	}
}
 // register as abstract because it is a singleton:
 WOSH_REGISTER_INTERFACE(wosh::BundleManager, "wosh::WoshModule", 0.01, _static_BundleManager )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BundleManager::BundleManager( const WoshKernel* parent )
	: WoshModule(parent, _BundleManager_NAME, _BundleManager_URI) {

//Log.setLevel( LOG_VERBOSE );

	Log(LOG_DEBUG, " Initializing Properties.." );
	Properties.set( _BundleManager_KEY_Count, 0, Permission( Permission::Read ) );
	Properties.set( _BundleManager_KEY_AutoLoadBundles,	false, Permission( Permission::RW, Permission::Read, Permission::Read ) );
	Properties.set( _BundleManager_KEY_AutoStartBundles,false, Permission( Permission::RW, Permission::Read, Permission::Read ) );
	Properties.set( _BundleManager_KEY_BundleConfigFolder, "$CONFIG/bundles-enabled", Permission( Permission::RW, Permission::Read, Permission::Read ) );
	Properties.set( _BundleManager_KEY_BundleLoadFolder, "$CONFIG/bundles-enabled", Permission( Permission::RW, Permission::Read, Permission::Read ) );
	Properties.set( _BundleManager_KEY_BundleLibFolder, "$ROOT/lib", Permission( Permission::RW, Permission::Read, Permission::Read ) );

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodRequest* mmListSupportedBundles = Methods.create<MethodRequest>( _BundleManager_METHOD_ListSupportedBundles, "List supported Bundles", Permission(Permission::RX) );
	mmListSupportedBundles->setMethod( this, (MethodRequestPtr)&BundleManager::mmDoListSupportedBundles );

	MethodMessageResponse* mmLoadBundle = Methods.create<MethodMessageResponse>( _BundleManager_METHOD_LoadBundle, "Load a Bundle", Permission::RW_RW_R__() );
	mmLoadBundle->setMethod( this, (MethodMessageResponsePtr)&BundleManager::mmDoLoadBundle );

	MethodMessageResponse* mmControlBundle = Methods.create<MethodMessageResponse>( _BundleManager_METHOD_control_bundle, "control a Bundle", Permission::RW_RW_R__() );
	mmControlBundle->setMethod( this, (MethodMessageResponsePtr)&BundleManager::mmDoControlBundle );

	MethodList* mmListBundles = Methods.create<MethodList>( _METHOD_List, "List installed Bundles", Permission::R_XR_XR__() );
	DataAdapterListT<Bundle>* dataAdapter = new DataAdapterListT<Bundle>(this->bundles);
	DataFieldCollector<Bundle>* dataFields = BundleGeneric::getDataFields();
	dataFields->setDataSetName("Installed Bundles");
	mmListBundles->setDataFieldExtractor( new DataFieldExtractor<Bundle>(dataAdapter, dataFields) );
 }

BundleManager::~BundleManager() {
	Log(LOG_DEBUG, ":~BundleManager() Destroying.." );

	Log(LOG_VERBOSE, ":~BundleManager() : Stopping Bundles.." );
	stopBundles();

	// delete remaining bundles (such the ones are created inline by user)
	Log(LOG_VERBOSE, ":~BundleManager() : Freeing Bundles [%d]..", this->bundles.count() );
	this->bundles.clear();

	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS

WRESULT BundleManager::onKernelInitializing() {
	WRESULT ret = WoshModule::onKernelInitializing();

	bool configured = false;
	if ( Properties.exists(_BundleManager_KEY_BundleConfigFolder) ) {
		string cfgPath = Properties.getValueOf(_BundleManager_KEY_BundleConfigFolder).toString();
		ret += WoshKernel::getInstance()->settings().loadConfigurationFolder( cfgPath );
		if ( WFAILED(ret) )
			Log(LOG_CRITICAL, ":init() FAILED#%d Loading Config Folder!", ret );
		else
			configured = true;
	 }
	bool autoLoad = Properties.getValueOf(_BundleManager_KEY_AutoLoadBundles).toBoolean(false);
	if ( autoLoad && !configured ) {
		Log(LOG_WARNING, ":init() AutoLoading Bundles is enabled  but had Errors Loading Config Folder!" );
		autoLoad = false;
		ret += WRET_ERR_PARAM;
	 }

	bool autostart = Properties.getValueOf(_BundleManager_KEY_AutoStartBundles).toBoolean(false);
	if ( autostart && !autoLoad ) {
		Log(LOG_WARNING, ":init() AutoStarting Bundles is enabled but had Errors Loading Config Folder/Loading Bundles!" );
		ret += WRET_ERR_PARAM;
	 }

	return ret;
 }
WRESULT BundleManager::onKernelInitialized() {
	WRESULT ret = WoshModule::onKernelInitialized();
	bool autoLoad = Properties.getValueOf(_BundleManager_KEY_AutoLoadBundles).toBoolean(false);
	if ( !autoLoad )
		return ret;

	Log(LOG_VERBOSE, ":doAutoLoadBundles() : Selecting Property "_BundleManager_KEY_BundleLoadFolder );
	string local_folder = Properties.getValueOf(_BundleManager_KEY_BundleLoadFolder).toString();
	ret = doAutoLoadBundles(local_folder);
	// apply settings before starting!!
	ret = applySettingsOfBundles( &WoshKernel::getInstance()->settings() );
	return ret;
 }
WRESULT BundleManager::onKernelRunning() {
	WRESULT ret = WoshModule::onKernelRunning();
	bool autostart = Properties.getValueOf(_BundleManager_KEY_AutoStartBundles).toBoolean(false);
	if ( !autostart )
		return ret;

	ret = this->loader.executeActions( BundleAction::ACTION_START );
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":doAutoStart() FAILED#%d Appling LOAD Actions!", ret );
	 }
	return ret;
 }
WRESULT BundleManager::onKernelStopping() {
	WRESULT ret = WoshModule::onKernelStopping();
	Log(LOG_INFO, ":onKernelStopping() Stopping Bundles.." );
	ret += stopBundles();
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":onKernelStopping() ERROR#%d Stopping Bundles.", ret );
	return ret;
 }
WRESULT BundleManager::onKernelStopped() {
	WRESULT ret = WoshModule::onKernelStopped();

	ret = unloadBundles();

	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BundleManager::doAutoLoadBundles( const string& cfgPath )
 {
	Log(LOG_INFO, ":doAutoLoadBundles(%s) : AutoLoading Bundles..", cfgPath.c_str() );
	WRESULT bundles_confs = this->loader.loadActionsFolder( cfgPath );
	if ( bundles_confs != WRET_OK ) {
		Log(LOG_CRITICAL, ":doAutoLoadBundles() FAILED#%d Loading Load Folder %s!", bundles_confs, cfgPath.c_str() );
	 }
	// create&register Bundles
	WRESULT bundles_exec = this->loader.executeActions( BundleAction::ACTION_LOAD );
	if ( bundles_exec != WRET_OK ) {
		Log(LOG_CRITICAL, ":doAutoLoadBundles() FAILED#%d Appling LOAD Actions!", bundles_exec );
	 }
	return bundles_exec;
 }

WRESULT BundleManager::applySettingsOfBundles( Settings* settings ) {
	if ( settings == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;
	this->bundles.transactionBeginRead();
	tBundleList::Iterator it;
	const tBundleList::ConstIterator it_end = this->bundles.end();
	for ( it=this->bundles.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		ret += BundleAction::applySettingTo( *it );
	 }
	this->bundles.transactionEnd();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BundleManager::installBundle( Bundle* bundle, bool start_bundle, bool applySettings, unsigned long timeout ) {
	if ( bundle == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;
	Log(LOG_INFO, ":installBundle(%s) : Installing..", bundle->getName().c_str() );
	// register bundle to BundleManager
	this->bundles.transactionBeginWrite();
	ret = registerBundle_( bundle );
	if ( WFAILED(ret) ) {
		this->bundles.transactionEnd();
		Log(LOG_CRITICAL, ":installBundle(%s) : FAILED#%d Registering!", bundle->getName().c_str(), ret );
		return ret;
	 }
	// apply related default settings if any
	if ( applySettings )
		ret = BundleAction::applySettingTo( bundle );

	if ( !start_bundle ) {
		this->bundles.transactionEnd();
		return ret;
	 }
	Log(LOG_VERBOSE, ":installBundle(%s) : Starting..", bundle->getName().c_str() );
	BundleActionStart* action = new BundleActionStart(Log);
	action->bundle_name = bundle->getName();
	this->bundles.transactionEnd();

	action->execute();
	ret = action->result_ret;
	if ( WFAILED(ret) )
		Log(LOG_VERBOSE, ":installBundle(%s) : ERROR#%d Starting!", bundle->getName().c_str(), action->result_ret );
	delete action;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BundleManager::loadBundle( const string& bundle_type, const string& bundle_name, bool applySettings ) {
	WRESULT ret = WRET_OK;
	Log(LOG_INFO, ":loadBundle(%s,%s) : Installing..", bundle_type.c_str(), bundle_name.c_str() );
	BundleActionLoad* action = new BundleActionLoad(Log);
	action->bundle_type.name = bundle_type;
	action->bundle_name = bundle_name;
	action->apply_settings = applySettings;
	action->execute();
	ret = action->result_ret;
	if ( WFAILED(ret) )
		Log(LOG_VERBOSE, ":loadBundle(%s,%s) : ERROR#%d Starting!", bundle_type.c_str(), bundle_name.c_str(), action->result_ret );
	delete action;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BundleManager::registerBundle( Bundle* bundle ) {
	MutexLockerWrite mL(this->bundles.getMutex());
	return registerBundle_(bundle);
}

WRESULT BundleManager::registerBundle_( Bundle* bundle ) {
	if ( bundle == NULL ) return WRET_ERR_PARAM;
	if ( this->bundles.exists(bundle) ) {
		this->bundles.transactionEnd();
		SystemError::raise(this, Log, SystemError::WARNING, 0, "registerBundle", "FAILED: Bundle '%s' already registered", bundle->getName().c_str() );
		return WRET_ERR_ILLEGAL_USE;
	 }
	tBundleList::ConstIterator it_f = std::find_if(this->bundles.begin(), this->bundles.end(),
		Functor_Validator<Bundle, const std::string&>(bundle->getName(), &Bundle::getName ) );
	if ( it_f != this->bundles.end() ) {
		SystemError::raise(this, Log, SystemError::WARNING, 0, "registerBundle", "WARNING: Bundle '%s' already registered", bundle->getName().c_str() );
		return WRET_ERR_PARAM;
	 }
	bundle->setParentObject(this);
	bundle->setPermission( Object::getPermission() );
	if ( !bundle->getURI().isGlobal() ) {
		bundle->getURI().setKernelLocal();
		bundle->getURI().setParent( this->getURI() );
		bundle->getURI().setName( bundle->getName() );
		bundle->getURI().registerGlobal();
	}
	Log(LOG_INFO, ":registerBundle(%s) : Adding to list..", bundle->getName().c_str() );
	this->bundles.push_back(bundle);
	Properties.transactionBeginWrite();
	Properties.update( _BundleManager_KEY_Count, this->bundles.size() );
	Properties.transactionEnd();
	return WRET_OK;
 }

WRESULT BundleManager::unRegisterBundle( Bundle* bundle )
 {
	if ( bundle == NULL ) return WRET_ERR_PARAM;

	this->bundles.transactionBeginWrite();
	if ( !this->bundles.exists(bundle) ) {
		this->bundles.transactionEnd();
		SystemError::raise(this, Log, SystemError::WARNING, 0, "unRegisterBundle", "FAILED: Bundle '%s' not found", bundle->getName().c_str() );
		return WRET_ERR_PARAM;
	 }
	Log(LOG_INFO, ":unRegisterBundle(%s) : Removing from list..", bundle->getName().c_str() );
	if ( bundle->getURI().isGlobal() )
		bundle->getURI().unRegisterGlobal();
	this->bundles.pop(bundle);
	Properties.transactionBeginWrite();
	Properties.update( _BundleManager_KEY_Count, this->bundles.size() );
	Properties.transactionEnd();
	this->bundles.transactionEnd();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BundleManager::startBundles()
 {
	Log(LOG_INFO, ":startBundles() : Starting.." );
	WRESULT ret = WRET_OK;
	tBundleActionList actions;
	ret = this->loader.createActions( actions, Bundle::STATE_STARTED );
	ret = this->loader.executeActions( actions );
	actions.free();
	return ret;
 }

WRESULT BundleManager::stopBundles()
 {
	Log(LOG_INFO, ":stopBundles() : Stopping.." );
	WRESULT ret = WRET_OK;
	tBundleActionList actions;
	ret = this->loader.createActions( actions, Bundle::STATE_STOPPED );
	ret = this->loader.executeActions( actions );
	actions.free();
	return ret;
 }

WRESULT BundleManager::unloadBundles()
 {
	Log(LOG_INFO, ":unloadBundles() : UnLoading.." );
	WRESULT ret = WRET_OK;
	tBundleActionList actions;
	ret = this->loader.createActions( actions, Bundle::STATE_DESTROYED );
	ret = this->loader.executeActions( actions );
	actions.free();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Bundle* BundleManager::getBundleByName_( const string& bundle_name ) const {
	tBundleList::ConstIterator it_f = std::find_if(this->bundles.begin(), this->bundles.end(),
		Functor_Validator<Bundle, const std::string&>(bundle_name, &Bundle::getName ) );
	if ( it_f == this->bundles.end() )
		return NULL;
	return *it_f;
 }
Bundle* BundleManager::getBundleByType_( const string& bundle_type ) const {
	tBundleList::ConstIterator it;
	const tBundleList::ConstIterator it_end = this->bundles.end();
	for ( it=this->bundles.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( (*it)->bundleInterfaces() == NULL ) continue;
		if ( (*it)->bundleInterfaces()->existsClass(bundle_type) )
			return *it;
	 }
	return NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE EVENTS

void BundleManager::bundle_event( Bundle::BUNDLE_STATE event, Bundle* bundle )
 {
	if ( bundle == NULL ) return;
	Log(LOG_VERBOSE, ":bundle_event() : Event #%d from %s", (int)event, bundle->getName().c_str() );
	switch(event) {
		case Bundle::STATE_CREATED: {	break; }
		case Bundle::STATE_STARTED: {
//				this->msgQueue.push( new Message( new Fact( "bundle_started", new Variant(bundle->getName()) ) ) );
				break;
			 }
		case Bundle::STATE_STOPPED: {
//				this->msgQueue.push( new Message( new Fact( "bundle_stopped", new Variant(bundle->getName()) ) ) );
				break;
			 }
		case Bundle::STATE_DESTROYED: {
//				this->unRegisterBundle( bundle );
				break;
			 }
		case Bundle::STATE_FAILURE: {
//				this->msgQueue.push( new Message( new Fact( "bundle_failure", new Variant(bundle->getName()) ) ) );
				break;
			 }
		default: { break; }
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* BundleManager::mmDoControlBundle( const Message* requestMessage )
 {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	// check rigths
	Variant dBundleName; Variant dAction;
	request->extractArguments(dBundleName, dAction);
	if ( !dBundleName.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid bus_name");
	this->bundles.transactionBeginRead();
	Bundle* bundle = getBundleByName_( dBundleName.asString() );
	if ( bundle == NULL ) {
		this->bundles.transactionEnd();
		return request->replyResponse(WRET_ERR_PARAM, "invalid bus_name");
	 }
	WRESULT ret = WRET_ERR_INTERNAL;
	if ( dAction == (int)Bundle::STATE_STARTED || dAction == "start" ) {
		if ( !bundle->isBundleRunning() )
			ret = bundle->bundleStart(); // asynch??
	 }
	else if ( dAction == (int)Bundle::STATE_STOPPED || dAction == "stop" ) {
		if ( bundle->isBundleRunning() )
			ret = bundle->bundleStop(); // asynch??
	 }
	else if ( dAction == "restart" ) {
		if ( bundle->isBundleRunning() )
			ret = bundle->bundleRestart(); // asynch??
	 }
	this->bundles.transactionEnd();
	return request->replyResponse(ret);
 }

Response* BundleManager::mmDoLoadBundle( const Message* requestMessage )
 {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	///@todo check rigths
	Variant dBundleType;
	Variant dBundleName;
	request->extractArguments(dBundleType, dBundleName);
	if ( !dBundleType.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid bundle type");
	BundleActionLoad load_bundle(Log);
	load_bundle.bundle_type.name = dBundleType.asString();
	if ( dBundleName.isStringNotEmpty() )
		load_bundle.bundle_name = dBundleName.asString();
	Log(LOG_INFO, ":mmDoLoadBundle(%s) Start", request->getArgument().toString().c_str() );
	// synch but in another thread, else it would dead-lock on bus-connection (setBundleState)
//	load_bundle.executeAsynch(20000);
	Log(LOG_VERBOSE, ":mmDoLoadBundle(%s) Done", request->getArgument().toString().c_str() );
	WRESULT ret = load_bundle.result_ret;
	return request->replyResponse(ret, load_bundle.bundle_name.c_str() );
 }

Response* BundleManager::mmDoListSupportedBundles( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListSupportedBundles(%s)", option.c_str() );
	std::vector<ObjectTypeInfo> class_list;
	WRESULT ret = ObjectFactory::findChildsOf( class_list, Bundle::className(), 0.0, true );
	if ( WFAILED(ret) )
		return request->replyResponse(WRET_ERR_INTERNAL, "Failed getting Type-list");
	std::vector<ObjectTypeInfo>::const_iterator it;
	if ( option == "s" || option == "short" ) { 
		List* objs = new List(class_list.size());
		for ( it=class_list.begin(); it!=class_list.end(); ++it ) {
			objs->push_back<Variant>( (*it).name );
		 }
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		Table* tbl = new Table( class_list.size(), 4, true );
		tbl->setTableName("Supported Bundles");
		tbl->setHeaderHorizontal("Name", 0);
		tbl->setHeaderHorizontal("Main Interface", 1);
		tbl->setHeaderHorizontal("Version", 2);
		tbl->setHeaderHorizontal("Installed", 3);
		unsigned int iRow = 0;
		this->bundles.transactionBeginRead();
		for ( it=class_list.begin(); it!=class_list.end(); ++it, ++iRow ) {
			tbl->set<Variant>( (*it).name, iRow, 0);
			tbl->set<Variant>( (*it).parent, iRow, 1);
			tbl->set<Variant>( (*it).version, iRow, 2);
			if ( getBundleByType_((*it).name) != NULL )
				tbl->set( new Variant(true), iRow, 3);
			else
				tbl->set( new Variant(false), iRow, 3);
		 }
		this->bundles.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// STATIC MEMBERS

bool BundleManager::waitState( const Bundle* bundle, Bundle::BUNDLE_STATE state, unsigned long timeout )
 {
	if ( bundle == NULL ) return false;
	if ( timeout > 0 )
		timeout = (unsigned long)(timeout / 10) + 1;
	unsigned long i = 0;
	while( ++i < timeout ) {
		if ( bundle->getBundleState() == state )
			return true;
		if ( state != Bundle::STATE_FAILURE && bundle->getBundleState() == Bundle::STATE_FAILURE )
			return false;
		ThreadImpl::sleepForMSec(10);
	 }
	if ( timeout > 0 )
		SystemError::raise(BundleManager::getInstance(),SystemError::CRITICAL,_BundleManager_ERROR_waitstate,"waitState");
	return ( bundle->getBundleState() == state );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
